package io.kwdb.sdk;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Represents the result of a query operation. Contains column metadata and rows of data. */
public class QueryResult {

  /** Represents a column in the result set. */
  public static class Column {
    private final String name;
    private final String type;

    public Column(String name, String type) {
      this.name = name;
      this.type = type;
    }

    public String getName() {
      return name;
    }

    public String getType() {
      return type;
    }

    @Override
    public String toString() {
      return "Column{name='" + name + "', type='" + type + "'}";
    }
  }

  /** Represents a row in the result set. */
  public static class Row {
    private final Map<String, Object> values;

    public Row() {
      this.values = new HashMap<>();
    }

    public Row(Map<String, Object> values) {
      this.values = new HashMap<>(values);
    }

    public void setValue(String columnName, Object value) {
      values.put(columnName, value);
    }

    public Object getValue(String columnName) {
      return values.get(columnName);
    }

    public String getString(String columnName) {
      Object value = values.get(columnName);
      return value != null ? value.toString() : null;
    }

    public Long getLong(String columnName) {
      Object value = values.get(columnName);
      if (value == null) return null;
      if (value instanceof Long) return (Long) value;
      if (value instanceof Number) return ((Number) value).longValue();
      if (value instanceof String) {
        try {
          return Long.parseLong((String) value);
        } catch (NumberFormatException e) {
          return null;
        }
      }
      return null;
    }

    public Double getDouble(String columnName) {
      Object value = values.get(columnName);
      if (value == null) return null;
      if (value instanceof Double) return (Double) value;
      if (value instanceof Number) return ((Number) value).doubleValue();
      if (value instanceof String) {
        try {
          return Double.parseDouble((String) value);
        } catch (NumberFormatException e) {
          return null;
        }
      }
      return null;
    }

    public Boolean getBoolean(String columnName) {
      Object value = values.get(columnName);
      if (value == null) return null;
      if (value instanceof Boolean) return (Boolean) value;
      if (value instanceof String) {
        return Boolean.parseBoolean((String) value);
      }
      return false;
    }

    public Map<String, Object> getValues() {
      return new HashMap<>(values);
    }

    @Override
    public String toString() {
      return "Row{" + values + '}';
    }
  }

  private final Status status;
  private final List<Column> columns;
  private final List<Row> rows;
  private final int affectedRows;

  private QueryResult(Status status, List<Column> columns, List<Row> rows, int affectedRows) {
    this.status = status;
    this.columns = new ArrayList<>(columns);
    this.rows = new ArrayList<>(rows);
    this.affectedRows = affectedRows;
  }

  // Getters
  public Status getStatus() {
    return status;
  }

  public List<Column> getColumns() {
    return new ArrayList<>(columns);
  }

  public List<Row> getRows() {
    return new ArrayList<>(rows);
  }

  public int getRowCount() {
    return rows.size();
  }

  public int getColumnCount() {
    return columns.size();
  }

  public int getAffectedRows() {
    return affectedRows;
  }

  public boolean isSuccess() {
    return status.isOk();
  }

  public boolean hasRows() {
    return !rows.isEmpty();
  }

  // Typed accessor methods for compatibility
  public Status getError() {
    return status.isOk() ? null : status;
  }

  public Map<String, Object> getRow(int index) {
    if (index < 0 || index >= rows.size()) {
      return new HashMap<>();
    }
    return rows.get(index).getValues();
  }

  public String getString(int rowIndex, String columnName) {
    if (rowIndex < 0 || rowIndex >= rows.size()) {
      return null;
    }
    return rows.get(rowIndex).getString(columnName);
  }

  public Long getLong(int rowIndex, String columnName) {
    if (rowIndex < 0 || rowIndex >= rows.size()) {
      return null;
    }
    return rows.get(rowIndex).getLong(columnName);
  }

  public Double getDouble(int rowIndex, String columnName) {
    if (rowIndex < 0 || rowIndex >= rows.size()) {
      return null;
    }
    return rows.get(rowIndex).getDouble(columnName);
  }

  public Boolean getBoolean(int rowIndex, String columnName) {
    if (rowIndex < 0 || rowIndex >= rows.size()) {
      return null;
    }
    return rows.get(rowIndex).getBoolean(columnName);
  }

  // Static factory methods
  public static QueryResult success(List<Column> columns, List<Row> rows) {
    return new QueryResult(Status.ok(), columns, rows, 0);
  }

  public static QueryResult success(List<Column> columns, List<Row> rows, int affectedRows) {
    return new QueryResult(Status.ok(), columns, rows, affectedRows);
  }

  public static QueryResult empty() {
    return new QueryResult(Status.ok(), new ArrayList<>(), new ArrayList<>(), 0);
  }

  public static QueryResult error(Status status) {
    return new QueryResult(status, new ArrayList<>(), new ArrayList<>(), 0);
  }

  public static QueryResult error(String message) {
    return new QueryResult(Status.internal(message), new ArrayList<>(), new ArrayList<>(), 0);
  }

  // Builder for complex result construction
  public static Builder builder() {
    return new Builder();
  }

  public static class Builder {
    private Status status = Status.ok();
    private List<Column> columns = new ArrayList<>();
    private List<Row> rows = new ArrayList<>();
    private int affectedRows = 0;

    public Builder status(Status status) {
      this.status = status;
      return this;
    }

    public Builder addColumn(String name, String type) {
      columns.add(new Column(name, type));
      return this;
    }

    public Builder addColumn(Column column) {
      columns.add(column);
      return this;
    }

    public Builder columns(List<Column> columns) {
      this.columns = new ArrayList<>(columns);
      return this;
    }

    public Builder addRow(Row row) {
      rows.add(row);
      return this;
    }

    public Builder addRow(Map<String, Object> values) {
      rows.add(new Row(values));
      return this;
    }

    public Builder rows(List<Row> rows) {
      this.rows = new ArrayList<>(rows);
      return this;
    }

    public Builder affectedRows(int affectedRows) {
      this.affectedRows = affectedRows;
      return this;
    }

    public QueryResult build() {
      return new QueryResult(status, columns, rows, affectedRows);
    }
  }

  @Override
  public String toString() {
    return "QueryResult{"
        + "status="
        + status
        + ", columns="
        + columns.size()
        + ", rows="
        + rows.size()
        + ", affectedRows="
        + affectedRows
        + '}';
  }

  /** Print result in table format for debugging. */
  public String toTableString() {
    if (!isSuccess()) {
      return "Error: " + status.getMessage();
    }

    if (columns.isEmpty()) {
      return "Empty result set";
    }

    StringBuilder sb = new StringBuilder();

    // Header
    for (Column col : columns) {
      sb.append(String.format("%-20s", col.getName()));
    }
    sb.append("\n");

    // Separator
    for (int i = 0; i < columns.size(); i++) {
      sb.append("--------------------");
    }
    sb.append("\n");

    // Data rows
    for (Row row : rows) {
      for (Column col : columns) {
        Object value = row.getValue(col.getName());
        String strValue = value != null ? value.toString() : "NULL";
        if (strValue.length() > 20) {
          strValue = strValue.substring(0, 17) + "...";
        }
        sb.append(String.format("%-20s", strValue));
      }
      sb.append("\n");
    }

    sb.append("Total: ").append(rows.size()).append(" rows\n");

    return sb.toString();
  }
}
